import numpy as np

# Numpy是高性能科学计算和数据分析的基础包。其部分功能如下
# 1、ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组
# 2、用于对整组数据进行快速运算的标准数学函数（无需编写循环）
# 3、用于读写磁盘数据的工具以及用于操作内存映射文件的工具
# 4、线性代数、随机数生成以及傅里叶变换功能
# 5、用于集成由C、C++、Fortran等语言编写的代码的工具
# Numpy提供了一个简单易用的C API。可以很容易将数据传递给低级语言编写的外部库，
# 外部库也能以numpy数组的形式将数据返回给Python
# numpy本身并没有提供多么高级的数据分析功能，理解numpy数组以及面向数组的计算
# 将有助于你更加高效地使用诸如pandas之类的工具
#
# 对于大部分数据分析应用而言，我最关注的的功能主要集中在
# 1、用于数据整理和清理、自己构造和过滤、转换等快速的矢量化数组运算
# 2、常用的数组算法，如排序、唯一化、集合运算等
# 3、高效的描述统计和数据集合/摘要运算
# 4、用于异构数据集的合并/连接运算的数据对齐和关系型数据运算
# 5、将条件逻辑表达为数组表达式（而不是带有if-elid-else分支的循环）
# 6、数组的分组运算（聚合、转换、函数应用等）
#
# NumPy的ndarray:一种多维数组对象
# NumPy最重要的一个特点就是其N维数组对象（ndarray），是一个快速而灵活的
# 大数据集容器。可以利用其对整块数组执行一些数学运算，其语法跟标量元素之间
# 的运算一样
#
# data
# print(data)
# data*10
# data + data
#
# ndarray是一个通用的同构数据多维容器，也就是说，其中所有元素必须是相同类型的
# 每个数组都有一个shape（表示各维度大小的元组）和一个dtype(一个用于说明数组数据类型的对象)
# data.shape
# data.dtype
# 虽然大多数据分析工作不需要深入理解numpy，但是精通面向数组的编程和思维方式
# 是成为Python科学计算牛人的一大关键步骤
#
# 创建ndarray
# 最简单的办法就是使用array函数。
# 它接受一切序列型的对象（包括其他数组），然后产生一个新的含有传入数组的numpy数组
# 以一个列表的转换为例
data1 = [6, 7.5, 8, 0, 1]
arr1 = np.array(data1)
# print(arr1)
# 嵌套序列（比如由一组等长列表组成的列表）将会被转换为一个多维数组
data2 = [[1, 2, 3, 4], [5, 6, 7, 8]]
arr2 = np.array(data2)
# print(arr2)
# print(arr2.ndim)
# print(arr2.shape)
# print(arr2.dtype)
# 除非显式说明，np.array会尝试为新建的这个数组推断出一个较为合适的数据类型
# 数据类型保存在一个特殊的的type对象中
#
# 除np.array之外，还有一些函数也可以新建数组
# 比如，zeros和ones分别创建指定长度或形状的全0或全1数组
# empty可以创建一个没有任何具体值的数组
# 要用这些方法创建多维数组，只需传入一个表示形状的元组即可
# print(np.zeros(10))
# print(np.ones((3, 6)))
# print(np.empty((2, 3, 2)))
#
# empty返回的都是一些未初始化的垃圾值
#
# arange是Python内置函数range的数组版
# print(np.arange(15))
# 
# 数组创建函数，由于numpy关注的是数值计算，如果没有特别指定，一般为float64
# 数组函数.png、
# 
# ndarray的数据类型
# dtype是一个特殊的对象，含有ndarray将一块内存解释为特定数据类型所需的信息
# arr1 = np.array([1, 2, 3], dtype=np.float64)
# arr2 = np.array([1, 2, 3], dtype=np.int32)
# print(arr1.dtype)
# print(arr2.dtype)
# 
# numpy的数据类型
# numpy的数据类型.png
# numpy的数据类型_1.png
# 
# 可以通过ndarray的astype方法显式地转换其dtype
# arr = np.array([1, 2, 3, 4, 5])
# print(arr.dtype)
# float_arr = arr.astype(np.float64)
# print(float_arr.dtype)
# 
# 如果浮点数转换成整数，则小数部分将会被截断
# arr = np.array([3.7, -1.2, -2.6, 0.5, 12.9, 10.1])
# print(arr)
# arr.astype(np.int32)
# print(arr)
# 
# 如果某字符串表示的全是数字，也可以用astype将其转换为数值形式
# numeric_strings = np.array(['1.25', '-9.6', '42'], dtype=np.string)
# numeric_strings.astype(float)
# numpy会将Python类型映射到等价的dtype上
# 
# 数组的dtype还有另外一个用法
# int_array = np.arange(10)
# calibers = np.array([.22, .270, .357, .380, .44, .50], dtype=np.float64)
# int_array.astype(calibers.dtype)
# 
# 你还可以用简洁的类型代码来表示dtype
# empty_unit32 = np.empty(8, dtype='u4')
# 
# 注意：调用astype无论如何都会创建出一个新的数组（原始数据的一份拷贝），即使新dtype与旧dtype相同
# 
# 
# 数组与标量之间的运算
# 大小相等的数组之间的任何算术运算都会将运算应用到元素级
# arr = np.array([[1., 2., 3.], [4., 5., 6.]])
# arr * arr
# arr - arr
# 
# 数组与标量的算术运算也会将那个标量值传播到各个元素
# 1 / arr
# arr ** 0.5
# 
# 不同大小的数组之间的运算叫做广播
# 
# 
# 基本的索引和切片
# numpy数组的索引是一个内容丰富的主题，因为选取数据子集或单个元素的方式有很多
# 一维数组很简单，从表面还是那个看，它们跟Python列表的功能差不多
# arr = np.arange(10)
# arr[5]
# arr[5:8]
# arr[5:8] = 12
# 当你将一个标量值赋值给一个切片时，该值会自动传播到整个选区
# 跟列表最重要的区别时，数组切片是原始数组的视图
# 这意味着数据不会被复制，视图上的任何修改都会直接反映到源数组上
# arr_slice = arr[5:8]
# arr_slice[1] = 12345
# arr_sliece[:] = 64
# 如果你想要的是ndarray切片的一份副本而非视图，就需要显式地进行复制操作
# arr[5:8].copy()
# 
# 在一个二维数组中，各索引位置上的元素不再是标量而是一维数组
# arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# arr2d[2]
# 可以对各个元素进行递归访问
# 你也可以传入一个以逗号隔开的索引列表来选取单个元素
# arr2d[0][2]
# arr2d[0, 2]
# 
# 二维数组的索引方式
# 二维数组的索引方式.png
# 
# 在多维数组中，如果省略了后面的索引，则返回对象会是一个维度低一点
# 的ndarray(它含有高一级维度上的所有数据)
# 括号外面的维度是一维、二维、三维、四维之类的意思，而括号里面的应该理解为“轴”
# 这里指的是“返回的低纬数组含有原始高维数组某条轴上的所有数据”
# arr3d = np.array([[[1, 2, 3], [4, 5, 6]],[[7, 8, 9], [10, 11, 12]]])
# arr3d
# arr3d[0]是一个2*3数组
# 标量值和数组都可以被赋值给arr3d[0]
# old_values = arr3d[0].copy()
# arr3d[0] = 42
# arr3d[0] = old_values
# arr3d[1, 0]可以访问索引以（1，0）开头的那些值
# 在上面所有这些选取数组子集的例子中，返回的数组都是视图
# 
# 
# 切片索引
# ndarray的切片语法跟Python列表这样的一维对象差不多
# arr[1:6]
# 
# 高维度对象的花样更多，你可以在一个或多个轴上进行切片，
# 也可以跟整数索引混合使用
# arr2d[:2]
# 它沿着第0轴（即第一个轴）切片的
# 切片时沿着一个轴向选取元素的。你可以一次传入多个切片
# arr2d[:2, 1:]
# 这样只能得到相同维数的数组试图
# 通过索引和切片混合，可以得到低纬度的切片
# arr2d[1, :2]
# arr2d[2, :1]
# 注意："只有冒号"表示选取整个轴
# arr2d[:, :1]
# 自然，对切片表达式的赋值操作也会被扩散到整个选区
# arr2d[:2, 1:] = 0
# 
# 二维数组切片
# 二维数组切片.png
# 
# 布尔型索引
# 假设我们有一个用于存储数据的数组以及一个存储姓名的数组（含有重复项）
# 使用numpy.random中的randn函数生成一些正态分布的随机数据
# names = np.array(['Bob', 'Joe', 'Will', 'Bob', 'Will', 'Joe', 'Joe'])
# data = randn(7, 4)
# names
# data
# 假设每个名字都对应data数组中的一行，而我们想要选出对应于名字"Bob"
# 的多有行。跟算术运算一样，数组的比较运算也是矢量化的
# names == 'Bob'
# 这个布尔型数组可用于数组索引
# data[names == 'Bob']
# 布尔型数组的长度必须跟被索引的轴长度一致
# 此外，还可以将布尔型数组跟切片、整数（或整数序列）混合使用
# data[names == 'Bob', 2:]
# data[names == 'Bob', 3]
# 要选择除“Bob”以外的其他值，既可以使用不等于符号（!=）,也可以通过符号（-）
# 对条件进行否定
# names != 'Bob'
# data[-(names == 'Bob')]
# 选取这三个名字中的两个需要组合应用多个布尔条件，使用&（和）
# |（或）之类的布尔算术运算即可
# mask = (names == 'Bob') | (names == 'Will')
# mask
# data[mask]
# 通过布尔型索引选取数组中的数据，将总是创建数据的副本，即使返回一模一样的数组
# 通过布尔型数组设置值是一种经常用到的手段
# 为了将data中所有负值都设置为0
# data[data < 0] = 0
# data
# 通过一维布尔数组设置整行或列的值也很简单
# data[names != 'Joe'] = 7
# data
# 
# 
# 花式索引
# 是一个numpy术语，指的是利用整数数组进行索引。假设有一个8*4数组
# arr = np.empty((8,4))
# for i in range(8):
#     arr[i] = i
# arr
# 为了以特定顺序选取行子集，只需传入一个用于指定顺序的整数列表或ndarray即可
# arr[[4, 3, 0, 6]]
# 使用复述索引将会从末尾开始选取行
# arr[[-3, -5, -7]]
# 一次传入多个索引数组会有一点特别，它返回的是一个一维数组，其中的元素对应各个索引元组
# arr = np.arange(32).reshape((8, 4))
# arr
# arr[[1, 5, 7, 2], [0, 3, 1, 2]]
# 选出的元素是（1, 0），（5， 3）， （7， 1）， （2， 2）
# 选取矩阵的行列子集应该是矩形区域的形式，结果并不是，但下面的方法可以得到
# arr[[1, 5, 7, 2], [:, [0, 3, 1, 2]]]
# 另外一个办法是使用np.ix_函数，可以将两个一维整数数组转换为一个用于选取方形区域的索引器
# arr[np.ix_([1, 5, 7, 2], [0, 3, 1, 2])]
# 花式索引跟切片不一样，它总是将数据复制到新数组中
# 
# 
# 数组转置和轴对换
# 转置是重塑的一种特殊形式，返回的是源数据的视图（不会进行任何复制操作）
# 数组不仅有transpose方法，还有一个特殊的T属性
# arr = np.arange(15).reshape((3, 5))
# arr
# arr.T
# 在进行矩阵计算时，经常需要用到该操作，比如利用np.dot计算矩阵内积XTX
# arr = np.random.randn(6,3)
# np.dot(arr.T, arr)
# 对于高维数组，transpose需要得到一个由轴编号组成的元组才能对这些轴进行转置
# arr = np.arange(16).reshape((2, 2, 4))
# arr
# arr.transpose((1, 0, 2))
# 简单的转置可以使用.T，其实就是进行轴对换而已
# ndarray还有一个swapaxes方法，需要接受一对轴编号
# arr
# arr.swapaxes(1, 2)
# swapaxes也是返回源数据的视图（不会进行任何复制操作）
# 
# 
# 